﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Xml;
using DataCenter.Share;

namespace DataCenter.Interface.Model
{
    public abstract class ComponentEntity : BaseEntity
    {
        private int componentId;
        private string componentName;
        private ComponentTypes componentType;
        private string componentConfig;
        private string assemblyName;
        private string typeName;

        public int ComponentId
        {
            get { return componentId; }
            set { componentId = value; }
        }

        public string ComponentName
        {
            get { return componentName; }
            set { componentName = value; }
        }

        public ComponentTypes ComponentType
        {
            get { return componentType; }
            set { componentType = value; }
        }

        public string ComponentConfig
        {
            get { return componentConfig; }
            set { componentConfig = value; }
        }

        public string AssemblyName
        {
            get { return assemblyName; }
            set { assemblyName = value; }
        }

        public string TypeName
        {
            get { return typeName; }
            set { typeName = value; }
        }

        public static List<ComponentEntity> SearchAsList(SqlCommand command, string componentIds)
        {
            string commandString = "SELECT * FROM Component WHERE 1 = 1";
            if (!string.IsNullOrEmpty(componentIds))
                commandString += " AND ComponentId IN (" + componentIds + ")";
            commandString += " ORDER BY ComponentId";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;

            List<ComponentEntity> entityList = new List<ComponentEntity>();
            using (SqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    string assemblyName = Utils.GetReaderValue<string>(reader, "AssemblyName");
                    string typeName = Utils.GetReaderValue<string>(reader, "TypeName");

                    ComponentEntity component = CreateComponentEntity(assemblyName, typeName);
                    if (component != null)
                    {
                        component.Build(reader);
                        entityList.Add(component);
                    }
                }
            }
            return entityList;
        }
        public static Dictionary<int, ComponentEntity> Search(SqlCommand command, string componentIds)
        {
            string commandString = "SELECT * FROM Component WHERE 1 = 1";
            if (!string.IsNullOrEmpty(componentIds))
                commandString += " AND ComponentId IN (" + componentIds + ")";
            commandString += " ORDER BY ComponentId";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;

            Dictionary<int, ComponentEntity> dictComponent =
                new Dictionary<int, ComponentEntity>();
            using (SqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    string assemblyName = Utils.GetReaderValue<string>(reader, "AssemblyName");
                    string typeName = Utils.GetReaderValue<string>(reader, "TypeName");

                    ComponentEntity component = CreateComponentEntity(assemblyName, typeName);
                    if (component != null)
                    {
                        component.Build(reader);
                        if (!dictComponent.ContainsKey(component.componentId))
                            dictComponent.Add(component.componentId, component);
                    }
                }
            }
            return dictComponent;
        }
        public static ComponentEntity CreateComponentEntity(string assemblyName, string typeName)
        {
            if (!string.IsNullOrEmpty(assemblyName) && !string.IsNullOrEmpty(typeName))
            {
                string fullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, assemblyName);
                if (File.Exists(fullPath))
                {
                    Assembly assembly = Assembly.LoadFile(fullPath);
                    object o = assembly.CreateInstance(typeName, true);
                    if (o != null)
                    {
                        return (ComponentEntity)o;
                    }
                }
            }
            return null;
        }
        public abstract TComponent CreateComponent<TComponent>(SqlCommand command);
        public abstract bool Create(SqlCommand command);
        public abstract bool Modify(SqlCommand command);
        public virtual bool Drop(SqlCommand command) 
        {
            Delete(command);
            return true;
        }
        public virtual void Insert(SqlCommand command)
        {
            string relativeName = assemblyName.Replace(AppDomain.CurrentDomain.BaseDirectory, "");

            string commandString = @"INSERT INTO Component(ComponentName, ComponentType, ComponentConfig, AssemblyName, TypeName)
                                   VALUES(@ComponentName, @ComponentType, @ComponentConfig, @AssemblyName, @TypeName)";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@ComponentName", componentName);
            command.Parameters.AddWithValue("@ComponentType", (int)componentType);
            command.Parameters.AddWithValue("@ComponentConfig", componentConfig);
            command.Parameters.AddWithValue("@AssemblyName", relativeName);
            command.Parameters.AddWithValue("@TypeName", typeName);
            command.ExecuteNonQuery();
            command.Parameters.Clear();

            command.CommandText = "SELECT @@identity";
            componentId = Convert.ToInt32(command.ExecuteScalar());
        }
        public virtual void IdentifyInsert(SqlCommand command)
        {
            string commandString = "SET IDENTITY_INSERT Component ON;";
            commandString += @"INSERT INTO Component(ComponentId, ComponentName, ComponentType, ComponentConfig, AssemblyName, TypeName)
                             VALUES(@ComponentId, @ComponentName, @ComponentType, @ComponentConfig, @AssemblyName, @TypeName);";
            commandString += "SET IDENTITY_INSERT Component OFF;";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@ComponentId", componentId);
            command.Parameters.AddWithValue("@ComponentName", componentName);
            command.Parameters.AddWithValue("@ComponentType", (int)componentType);
            command.Parameters.AddWithValue("@ComponentConfig", componentConfig);
            command.Parameters.AddWithValue("@AssemblyName", assemblyName);
            command.Parameters.AddWithValue("@TypeName", typeName);
            command.ExecuteNonQuery();
            command.Parameters.Clear();
        }
        public virtual void Update(SqlCommand command)
        {
            string commandString = @"UPDATE Component 
                                    SET ComponentName=@ComponentName, 
                                        ComponentType=@ComponentType, 
                                        ComponentConfig=@ComponentConfig, 
                                        AssemblyName=@AssemblyName, 
                                        TypeName=@TypeName
                                    WHERE ComponentId=@ComponentId";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@ComponentId", componentId);
            command.Parameters.AddWithValue("@ComponentName", componentName);
            command.Parameters.AddWithValue("@ComponentType", (int)componentType);
            command.Parameters.AddWithValue("@ComponentConfig", componentConfig);
            command.Parameters.AddWithValue("@AssemblyName", assemblyName);
            command.Parameters.AddWithValue("@TypeName", typeName);
            command.ExecuteNonQuery();
            command.Parameters.Clear();
        }
        public virtual bool UpdateXmlConfig(SqlCommand command)
        {
            string commandString = @"UPDATE Component SET ComponentConfig=@ComponentConfig WHERE ComponentId=@ComponentId";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@ComponentId", componentId);
            command.Parameters.AddWithValue("@ComponentConfig", componentConfig);
            command.ExecuteNonQuery();
            command.Parameters.Clear();

            return true;
        }
        public virtual void Delete(SqlCommand command)
        {
            string commandString = @"DELETE FROM Component WHERE ComponentId=@ComponentId";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@ComponentId", componentId);
            command.ExecuteNonQuery();
            command.Parameters.Clear();
        }
        public virtual void Build(SqlDataReader reader)
        {
            componentId = Utils.GetReaderValue<int>(reader, "ComponentId");
            componentName = Utils.GetReaderValue<string>(reader, "ComponentName");
            componentType = Utils.GetReaderValue<ComponentTypes>(reader, "ComponentType");
            componentConfig = Utils.GetReaderValue<string>(reader, "ComponentConfig");
            assemblyName = Utils.GetReaderValue<string>(reader, "AssemblyName");
            typeName = Utils.GetReaderValue<string>(reader, "TypeName");

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(componentConfig);
            FromXml(xmlDoc.ChildNodes[0]);      
        }
        public virtual void FromXml(XmlNode xnlConfig)
        {
            Type theType = this.GetType();
            foreach (PropertyInfo propInfo in theType.GetProperties())
            {
                if (Attribute.IsDefined(propInfo, typeof(ParameterAttribute)))
                {
                    object[] attributes = propInfo.GetCustomAttributes(typeof(ParameterAttribute), false);
                    ParameterAttribute attr = (ParameterAttribute)attributes[0];

                    string paramName = propInfo.Name;
                    if (!string.IsNullOrEmpty(attr.ParamName))
                        paramName = attr.ParamName;
                    XmlNode xnlParam = xnlConfig.SelectSingleNode("PARAM[@Name='" + paramName + "']");
                    if (xnlParam != null)
                    {
                        string value = "";
                        switch (attr.StorageType)
                        {
                            case StorageTypes.StoreInAttribute:
                                XmlAttribute xalValue = xnlParam.Attributes["Value"];
                                if (xalValue != null) value = xalValue.Value;
                                break;                            
                            case StorageTypes.StoreInInnerText:
                            case StorageTypes.StoreInContent:
                                value = xnlParam.InnerText;
                                break;
                        }

                        object oValue = Utils.ConvertTo(value, propInfo.PropertyType);
                        if (oValue == null)
                        {
                            if (attr.DefaultValue != null)
                                oValue = attr.DefaultValue;
                        }
                        propInfo.SetValue(this, oValue, null);
                    }
                }
            }

        }
        public virtual XmlNode ToXml()
        {
            XmlDocument xmlDoc = new XmlDocument();
            XmlNode xnlConfig = xmlDoc.CreateElement("CONFIG");

            Type theType = this.GetType();
            foreach (PropertyInfo propInfo in theType.GetProperties())
            {
                if (Attribute.IsDefined(propInfo, typeof(ParameterAttribute)))
                {
                    object[] attributes = propInfo.GetCustomAttributes(typeof(ParameterAttribute), false);
                    ParameterAttribute attr = (ParameterAttribute)attributes[0];

                    XmlNode xnlParam = xmlDoc.CreateElement("PARAM");
                    XmlAttribute xalName = xmlDoc.CreateAttribute("Name");
                    xalName.Value = (!string.IsNullOrEmpty(attr.ParamName) ? attr.ParamName : propInfo.Name);
                    xnlParam.Attributes.Append(xalName);

                    object oValue = propInfo.GetValue(this, null);
                    if (oValue == null)
                    {
                        if (attr.DefaultValue != null)
                            oValue = attr.DefaultValue;
                    }
                    string value = oValue == null ? "" : oValue.ToString();
                    switch (attr.StorageType)
                    {
                        case StorageTypes.StoreInAttribute:
                            XmlAttribute xalValue = xmlDoc.CreateAttribute("Value");
                            xalValue.Value = value;
                            xnlParam.Attributes.Append(xalValue);
                            break;
                        case StorageTypes.StoreInInnerText:
                            xnlParam.InnerText = value;
                            break;
                        case StorageTypes.StoreInContent:
                            xnlParam.InnerXml = "<![CDATA[" + value + "]]>";
                            break;
                    }

                    xnlConfig.AppendChild(xnlParam);
                }
            }

            return xnlConfig;
        }
        public override string ToString()
        {
            return componentName + "(" + componentId + ")";
        }
    }
}
